Caching
Caching reduces the number of requests that need to hit the server, saving bandwidth, CPU cycles, and database queries. This is essential for highly scalable APIs, especially when serving thousands or millions of clients.
There are two main caching types in HTTP REST APIs:
- Client-side caching – handled by the client/browser.
- Server-side caching – handled by proxies or the server itself.
HTTP caching uses headers to communicate caching rules between clients and servers.
ETag
An ETag is a unique identifier (usually a hash or version string) for a specific version of a resource. The server generates it, and the client can use it to check if the resource has changed.
How it works
-
Client requests a resource:
GET /api/users/123 -
Server responds with the resource and an
ETagheader:HTTP/1.1 200 OK
ETag: "abc123"
Content-Type: application/json
{ "id": 123, "name": "Alice" } -
Next time, client asks if the resource has changed using
If-None-Match:GET /api/users/123
If-None-Match: "abc123" -
If resource hasn’t changed:
HTTP/1.1 304 Not Modified- No body is sent.
- Client can use cached version.
Benefits:
- Saves bandwidth.
- Avoids unnecessary data transfer.
- Improves API scalability.
Last-Modified
Last-Modified is an HTTP header indicating the last time the resource was changed. Clients can use it to validate cache freshness.
How it works:
-
Server responds with the Last-Modified header:
HTTP/1.1 200 OK
Last-Modified: Wed, 08 Jan 2026 10:00:00 GMT -
Client sends conditional request:
GET /api/users/123
If-Modified-Since: Wed, 08 Jan 2026 10:00:00 GMT -
Server checks:
- If resource has not changed, respond
304 Not Modified. - If resource has changed, respond
200 OKwith new content.
- If resource has not changed, respond
Benefits:
- Simple to implement.
- Works well when updates are time-based.
Cache-Control
Cache-Control gives explicit instructions on how long and under what conditions a response can be cached.
Common directives
| Directive | Description |
|---|---|
public | Response can be cached by any cache (client, proxy). |
private | Response can only be cached by client, not proxy. |
max-age=seconds | Maximum time (in seconds) the response is considered fresh. |
no-cache | Client must revalidate with server before using cached data. |
no-store | Never cache this response. |
Example in REST API:
GET /api/products
Response:
HTTP/1.1 200 OK
Cache-Control: public, max-age=3600
Content-Type: application/json
[
{ "id": 101, "name": "Laptop" },
{ "id": 102, "name": "Phone" }
]
- Client or proxy can cache this response for 1 hour.
- After 1 hour, it must check the server again.
Combination with ETag/Last-Modified:
- You can combine
Cache-ControlwithETagorLast-Modifiedfor conditional caching, which maximizes performance.
Practical Strategy for REST APIs
- Static resources (e.g., product catalog images)
- Use
Cache-Control: public, max-age=86400(1 day) - Use ETag for updates.
- Use
- Dynamic resources (e.g., user profiles)
- Use
Cache-Control: private, max-age=60(1 minute) - Use ETag or Last-Modified for conditional GETs.
- Use
- Sensitive resources (e.g., banking info)
- Use
Cache-Control: no-storeto avoid caching.
- Use
Full REST API Response with All Headers
GET /api/users/123
Response:
HTTP/1.1 200 OK
Content-Type: application/json
ETag: "a1b2c3"
Last-Modified: Wed, 08 Jan 2026 10:00:00 GMT
Cache-Control: private, max-age=300
{
"id": 123,
"name": "Alice",
"email": "alice@example.com"
}
Next request after 2 minutes:
GET /api/users/123
If-None-Match: "a1b2c3"
If-Modified-Since: Wed, 08 Jan 2026 10:00:00 GMT
- If unchanged →
304 Not Modified→ client uses cached data. - If changed →
200 OKwith new data → update cache.